package cn.zhz.privacy.kspapi;

import cn.hutool.http.HttpRequest;
import cn.hutool.setting.dialect.Props;
import com.alibaba.fastjson.JSONObject;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.util.*;

/**
 * @author linzhj
 * @date 2022年4月6日
 */
public class KspApi {

    /**
     * 字符编码
     */
    protected final static String CHARSET = "UTF-8";
    /**
     * 公共参数，接口版本
     */
    private static final String API_VERSION = "1.0";
    /**
     * 密码服务接口基础地址
     */
    private static String BASE_URL;
    /**
     * 密码服务平台应用管理里面的安全凭证
     */
    private static String SECRET_ID;
    /**
     * 密码服务平台应用管理里面的安全凭证
     */
    private static String SECRET_KEY;

    private static Integer retryCount;

    private static Integer connectTimeout;

    private static Integer timeout;


    static {
        Props props = new Props("application.properties");
        retryCount = props.getInt("retryCount");
        connectTimeout = props.getInt("connectTimeout");
        timeout = props.getInt("timeout");
    }

    /**
     * 初始化接口调用环境，此方法全局只需要调用一次即可
     *
     * @param baseUrl   密码服务接口基础地址
     * @param secretId  密码服务平台应用管理里面的安全凭证
     * @param secretKey 密码服务平台应用管理里面的安全凭证
     */
    public static void init(String baseUrl, String secretId, String secretKey) {
        BASE_URL = baseUrl;
        SECRET_ID = secretId;
        SECRET_KEY = secretKey;
    }

    /**
     * 将字节数组转换为十六进制字符数组
     */
    public static String toHex(byte[] data) {
        char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        final int len = data.length;
        final char[] out = new char[len << 1];// len*2
        for (int i = 0, j = 0; i < len; i++) {
            out[j++] = digits[(0xF0 & data[i]) >>> 4];// 高位
            out[j++] = digits[0x0F & data[i]];// 低位
        }
        return new String(out);
    }

    /**
     * 组装并发起HTTP请求
     *
     * @param url    接口地址
     * @param params 接口参数
     * @return 接口返回结果
     * @throws Exception
     */
    protected static JSONObject doHttpPost(String url, JSONObject params) throws Exception {

        if (BASE_URL == null || SECRET_ID == null || SECRET_KEY == null) {
            throw new RuntimeException("请先调用KspApi类的init方法，并正确传参");
        }
        Map<String, String> headers = new HashMap<>();
        String nonce = new Random().nextInt(Integer.MAX_VALUE) + "";
        String secretId = SECRET_ID;
        String timestamp = System.currentTimeMillis() + "";
        String version = API_VERSION;

        headers.put("nonce", nonce); // 公共参数
        headers.put("secretId", secretId); // 公共参数
        headers.put("timestamp", timestamp); // 公共参数
        headers.put("version", version); // 公共参数

        Map<String, Object> signMap = new HashMap<>();
        signMap.putAll(headers);
        signMap.putAll(params);
        String signature = sign(getStringToSign(signMap), SECRET_KEY);
        headers.put("signature", signature); // 公共参数，签名值


        String body = null;
        for (int i = 0; i < retryCount; i++) {
            try {
                body = HttpRequest.post(BASE_URL + url).setConnectionTimeout(connectTimeout).timeout(-1).addHeaders(headers).body(params.toJSONString()).execute().body();
                System.out.println(body);
            } catch (Exception ignored) {

            }
            if (body == null) {
                continue;
            }
            JSONObject jsonObject = JSONObject.parseObject(body);

            if (jsonObject.getString("status").equals("200")) {
                return jsonObject;
            } else {
                throw new RuntimeException("接口返回" + body);
            }

        }

        throw new RuntimeException("连接：" + BASE_URL + url + "失败");

    }

    /**
     * 组装并发起Form请求
     *
     * @param url    接口地址
     * @param params 接口参数
     * @return 接口返回结果
     * @throws Exception
     */
    protected static JSONObject doFormPost(String url, JSONObject params) throws Exception {
        if (BASE_URL == null || SECRET_ID == null || SECRET_KEY == null) {
            throw new RuntimeException("请先调用KspApi类的init方法，并正确传参");
        }
        Map<String, String> headers = new HashMap<>();
        String nonce = new Random().nextInt(Integer.MAX_VALUE) + "";
        String secretId = SECRET_ID;
        String timestamp = System.currentTimeMillis() + "";
        String version = API_VERSION;

        headers.put("nonce", nonce); // 公共参数
        headers.put("secretId", secretId); // 公共参数
        headers.put("timestamp", timestamp); // 公共参数
        headers.put("version", version); // 公共参数

        Map<String, Object> signMap = new HashMap<>();
        signMap.putAll(headers);
        signMap.putAll(params);
        String signature = sign(getStringToSign(signMap), SECRET_KEY);
        headers.put("signature", signature); // 公共参数，签名值
        headers.put("Content-Type", "multipart/form-data");

        String body = null;
        for (int i = 0; i < retryCount; i++) {
            try {
                body = HttpRequest.post(BASE_URL + url).setConnectionTimeout(connectTimeout).timeout(timeout).addHeaders(headers).body(params.toJSONString()).execute().body();
                System.out.println(body);
            } catch (Exception ignored) {

            }
            if (body == null) {
                continue;
            }
            JSONObject jsonObject = JSONObject.parseObject(body);

            if (jsonObject.getString("status").equals("200")) {
                return jsonObject;
            } else {
                throw new RuntimeException("接口返回" + body);
            }

        }

        throw new RuntimeException("连接：" + BASE_URL + url + "失败");
    }

    /**
     * 签名方法
     *
     * @param str       待签名字符串
     * @param secretKey 安全凭证key
     * @return 签名值
     */
    private static String sign(String str, String secretKey) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA1");
        SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(CHARSET), mac.getAlgorithm());
        mac.init(secretKeySpec);
        byte[] hash = mac.doFinal(str.getBytes(CHARSET));
        return base64Encode(hash);
    }

    /**
     * 组装待签名参数
     */
    private static String getStringToSign(Map<String, Object> params) {
        // 签名时要求对参数进行字典排序，此处用TreeMap保证顺序
        TreeMap<String, Object> treeMap = new TreeMap<>(params);
        StringBuilder sb = new StringBuilder();
        for (String k : treeMap.keySet()) {
            if (treeMap.get(k) != null && !(treeMap.get(k) instanceof File) && !(treeMap.get(k) instanceof List)) {
                sb.append(k).append("=").append(treeMap.get(k).toString()).append("&");
            }
        }
        return sb.toString().substring(0, sb.length() - 1);
    }

    /**
     * Base64编码
     *
     * @param input
     * @return
     */
    public static String base64Encode(byte[] input) {
        if (input == null) {
            return null;
        }
        return Base64.getEncoder().encodeToString(input);
    }

    /**
     * Base64解码
     *
     * @param input
     * @return
     */
    public static byte[] base64Decode(String input) {
        return Base64.getDecoder().decode(input);
    }

}
